package org.mzyyn.qr.service.auction.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import jakarta.annotation.Resource;
import org.apache.ibatis.jdbc.Null;
import org.checkerframework.checker.units.qual.A;
import org.mzyyn.qr.constants.RedisKeyConstant;
import org.mzyyn.qr.context.UserAuthContext;
import org.mzyyn.qr.dto.auction.BidDTO;
import org.mzyyn.qr.dto.auction.BidListDTO;
import org.mzyyn.qr.entity.AuctionGoodsPO;
import org.mzyyn.qr.enums.JifenCommonResultEnum;
import org.mzyyn.qr.service.auction.AuctionBidRecordService;
import org.mzyyn.qr.service.auction.AuctionGoodsService;
import org.mzyyn.qr.service.auction.AuctionService;
import org.mzyyn.qr.service.user.UserService;
import org.mzyyn.qr.vo.auction.BidListVO;
import org.mzyyn.qr.vo.auction.BidRedisVO;
import org.mzyyn.qr.vo.auction.BidVO;
import org.qr.xiaocao.common.enums.CommonResultEnum;
import org.qr.xiaocao.exception.ServiceException;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author lipeng
 * @description 拍卖实现
 * @date 2024/9/23 16:12
 */
@Service
public class AuctionServiceImpl implements AuctionService {
    @Autowired
    AuctionGoodsService auctionGoodsService;

    @Autowired
    UserService userService;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    AuctionBidRecordService auctionBidRecordService;

    @Resource
    Redisson redisson;

    @Override
    public BidVO bid(BidDTO bidDTO){
        //商品信息
        AuctionGoodsPO auctionGoodsPO = auctionGoodsService.checkAndGetGoodsById(bidDTO.getGoodId());

        //上锁
        RLock lock = redisson.getLock(RedisKeyConstant.AUCTION_BID_LOCK + auctionGoodsPO.getId());
        try {
            if( lock.tryLock(60,60, TimeUnit.SECONDS) ) {

                //是否结束
                if (auctionGoodsService.getEndTime(String.valueOf(auctionGoodsPO.getId()),auctionGoodsPO.getEndTime()).isBefore(LocalDateTime.now())) {
                    throw new ServiceException(CommonResultEnum.COMMON_FAILED.getCode(), "当前商品拍卖已结束");
                }

                String goodIdStr = String.valueOf(auctionGoodsPO.getId());

                BidRedisVO bidRedisVO = getLatestPrice(goodIdStr);//最新用户出价
                if(  null == bidRedisVO ){
                    //初始化 防止后面使用时一直判断null
                    bidRedisVO = new BidRedisVO();
                    bidRedisVO.setPrice(-1L);
                    bidRedisVO.setUuid(StrUtil.EMPTY);
                }

                String lastBidUserUuid = bidRedisVO.getUuid();
                //当前最新出价是否是自己
                if (checkLatestIsMe(lastBidUserUuid)) {
//                        throw new ServiceException(CommonResultEnum.COMMON_FAILED.getCode(), "不能连续出价");
                }

                //获取商品最新出价
                Long latestPrice = bidRedisVO.getPrice();
                Long nextPrice = bidDTO.getNextPrice(); //用户出价

                Long maxBeishu = 10L;
                Long minPrice = (auctionGoodsPO.getStartPrice() + auctionGoodsPO.getRaisePrice());//最低价
                Long maxPrice = auctionGoodsPO.getStartPrice() + auctionGoodsPO.getRaisePrice() * maxBeishu;
                if (-1 != latestPrice) {
                    minPrice = latestPrice + auctionGoodsPO.getRaisePrice();
                    maxPrice = latestPrice + auctionGoodsPO.getRaisePrice() * maxBeishu;
                }
                if (nextPrice < minPrice) {
                    throw new ServiceException(CommonResultEnum.COMMON_FAILED.getCode(), "价格过低，重新出价");
                }
                if (nextPrice > maxPrice) {
                    throw new ServiceException(CommonResultEnum.COMMON_FAILED.getCode(), "价格过高，重新出价");
                }

                //用户积分
                Long userIntegral = userService.getUserIntegral(null);
                if (userIntegral < nextPrice) {
                    throw new ServiceException(JifenCommonResultEnum.JIFENBUZU.getCode(), "积分不足");
                }

                //出价
                BidRedisVO currentBidRedisVORequest = new BidRedisVO();
                currentBidRedisVORequest.setUsername(UserAuthContext.getUserInfo().getUsername());
                currentBidRedisVORequest.setAvater(UserAuthContext.getUserInfo().getAvatar());
                currentBidRedisVORequest.setUuid(UserAuthContext.getUserInfo().getUuid());
                currentBidRedisVORequest.setPrice(nextPrice);
                currentBidRedisVORequest.setBidTime(DateUtil.now());
                stringRedisTemplate.opsForList().leftPush(
                        RedisKeyConstant.AUCTION_JOIN_RECORD + auctionGoodsPO.getId(),
                        JSON.toJSONString(currentBidRedisVORequest)
                );


                String userCoreMapRedisKey = RedisKeyConstant.USER_CORE_MAP + UserAuthContext.getUserInfo().getUuid();

                //冻结商品idkey
                String dongjieGoodsRedisKey = String.format(RedisKeyConstant.USER_JIFEN_MAP_DONGJIE_JIFEN, goodIdStr);

                //用户积分处理
                //最后出价用户积分恢复
                if (StrUtil.isNotEmpty(lastBidUserUuid)) {
                    String lastBidUserCoreMapRedisKey = RedisKeyConstant.USER_CORE_MAP + lastBidUserUuid;
                    Long lastBidUserDongjieIntegral = userService.getUserDongjieIntegral(lastBidUserUuid, goodIdStr);
                    stringRedisTemplate.opsForHash().delete(lastBidUserCoreMapRedisKey, dongjieGoodsRedisKey);
                    stringRedisTemplate.opsForHash().increment(lastBidUserCoreMapRedisKey, RedisKeyConstant.USER_JIFEN_MAP_JIFEN, lastBidUserDongjieIntegral);
                }

                //新出价用户积分冻结
                stringRedisTemplate.opsForHash().put(userCoreMapRedisKey, dongjieGoodsRedisKey, StrUtil.toStringOrNull(nextPrice));
                stringRedisTemplate.opsForHash().increment(userCoreMapRedisKey, RedisKeyConstant.USER_JIFEN_MAP_JIFEN, -nextPrice);

                //异步写入记录
                auctionBidRecordService.addBidRecord(auctionGoodsPO.getId(), UserAuthContext.getUserInfo().getUuid(), nextPrice);

                //通知前端

                //结束时间判断处理
                doProEndTime(auctionGoodsPO);


                return BeanUtil.toBean(currentBidRedisVORequest,BidVO.class);



            }else{
                BidVO bidVO = new BidVO();
                bidVO.setState(0);
                return bidVO;
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ServiceException e){
            throw e;
        }catch (Exception e){
            throw new ServiceException(CommonResultEnum.COMMON_FAILED.getCode(), e.getMessage());
        }finally {
            if( lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }

    /**
     * 结束时间
     */
    public void doProEndTime(AuctionGoodsPO auctionGoodsPO){
        //如果时间剩下最后5分钟了，延迟时间
        String goodEndTimeRedisKey = RedisKeyConstant.GOODS_END_TIME + auctionGoodsPO.getId();
        String endTime = stringRedisTemplate.opsForValue().get(goodEndTimeRedisKey);
        // 获取当前时间的秒级时间戳
        long secondsSinceEpoch = DateUtil.currentSeconds();
        long endTimeLong;
        if( StrUtil.isEmpty(endTime) ){
            // 获取毫秒级时间戳
            endTimeLong = DateUtil.date(auctionGoodsPO.getEndTime()).getTime()/ 1000;
            stringRedisTemplate.opsForValue().set(
                    goodEndTimeRedisKey,
                    StrUtil.toStringOrNull(endTimeLong)
            );
        }else{
            endTimeLong = Long.valueOf(endTime);
        }
        //300秒 5分钟，当前时间+10分钟
        if( secondsSinceEpoch + 300 > endTimeLong ){
            endTimeLong = secondsSinceEpoch + 10 * 60;
            stringRedisTemplate.opsForValue().set(
                    goodEndTimeRedisKey,
                    StrUtil.toStringOrNull(endTimeLong)
            );
        }
    }

    @Override
    public List<BidListVO> bidlist(BidListDTO bidListDTO) {
        //商品信息
        AuctionGoodsPO auctionGoodsPO = auctionGoodsService.checkAndGetGoodsById(bidListDTO.getGoodId());

        // 使用rangeByScore方法获取所有成员，参数含义为：从最小分数到最大分数，倒序排列
        List<String> res = stringRedisTemplate.opsForList().range(
                RedisKeyConstant.AUCTION_JOIN_RECORD + auctionGoodsPO.getId()
                , 0, -1);
        if(CollUtil.isEmpty(res)){
            return new ArrayList<>();
        }

        return res.stream().map(v->{
            return JSON.parseObject(v, BidListVO.class);
        }).collect(Collectors.toList());
    }

    /**
     * 校验最后一次是否是自己出价
     * @param lastUserUuid
     * @return
     */
    public boolean checkLatestIsMe(String lastUserUuid){
        if(StrUtil.isEmpty(lastUserUuid)){
            return false;
        }

        if( lastUserUuid.equals(UserAuthContext.getUserInfo().getUuid()) ){
            return true;
        }
        return false;
    }

    /**
     * 获取最新的出价
     * @param goodsId
     * @param uuid
     * @return
     */
    public BidRedisVO getLatestPrice(String goodsId) {
        String key = RedisKeyConstant.AUCTION_JOIN_RECORD + goodsId;

        // 使用ZREVRANGE获取分数最高的一个元素，ZREVRANGE是按照分数从高到低排序
        // 参数：key, 开始索引, 结束索引
        // 这里的参数"0"和"0"表示获取分数最高的一个元素
        String val = stringRedisTemplate.opsForList().index(key, 0);
        if( StrUtil.isEmpty(val) ){
            return null;
        }
        return JSON.parseObject(val, BidRedisVO.class);
    }

    public String getLatestZSetEntry(String goodsId) {

        String key = RedisKeyConstant.AUCTION_JOIN_RECORD + goodsId;

        // 使用ZREVRANGE获取分数最高的一个元素，ZREVRANGE是按照分数从高到低排序
        // 参数：key, 开始索引, 结束索引
        // 这里的参数"0"和"0"表示获取分数最高的一个元素
        Set<String> lastEntry = stringRedisTemplate.opsForZSet().reverseRange(key, 0, 0);

        // 返回获取到的元素，这里假设至少有一个元素存在
        return lastEntry.isEmpty() ? null : lastEntry.iterator().next();
    }
}
